/*
 * This file is part of LangMan.
 *
 * LangMan is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * LangMan is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with LangMan.  If not, see <http://www.gnu.org/licenses/>.
 */

package langman.business.io;

import javafx.collections.ObservableMap;
import langman.Utils;
import langman.business.data.TranslationException;
import langman.business.data.model.Category;
import langman.business.data.model.Key;
import langman.business.data.model.Language;
import langman.util.languagefile.commons.TranslationMode;
import langman.util.languagefile.reader.LanguageReader;
import langman.util.languagefile.reader.LanguageReaderEventHandler;

import java.io.File;
import java.util.Map;
import java.util.Objects;

@SuppressWarnings("AssignmentToCollectionOrArrayFieldFromParameter")
public class DataLoader implements LanguageReaderEventHandler {
    private final File file;
    private final Language language;
    private final int numberOfLanguages;
    private final Map<String, Category> categoryMap;
    private final Language referenceLanguage;
    private Category currentCategory;

    public DataLoader(int numberOfLanguages, String languageName, File languageFile, Map<String, Category> categoryMap, Language referenceLanguage) {
        this.numberOfLanguages = numberOfLanguages;
        this.categoryMap = categoryMap;
        this.referenceLanguage = referenceLanguage;
        language = new Language(languageName, languageFile.getName());
        file = languageFile;
    }

    public void load() throws TranslationException {
        try (LanguageReader reader = new LanguageReader(file)) {
            reader.addListener(this);
            reader.readToEnd();
        } catch (Exception e) {
            throw new TranslationException(e);
        }
    }

    public Language getLanguage() {
        return language;
    }

    @Override
    public void categoryChanged(String newCategory) {
        currentCategory = ensureUniqueCategory(newCategory);
    }

    @Override
    public void translationEncountered(String keyString, String value, TranslationMode translationMode) throws TranslationException {
        Key key = ensureSingularKey(keyString);
        translateIfRealTranslation(key, value, translationMode);
    }

    private Key ensureSingularKey(String keyString) throws TranslationException {
        ObservableMap<String, Key> keysUnmodifiable = currentCategory.getKeysUnmodifiable();
        if (!keysUnmodifiable.containsKey(keyString)) {
            Key key = new Key(numberOfLanguages, keyString);
            currentCategory.addKey(key);
        }

        Key key = keysUnmodifiable.get(keyString);

        String translation = key.getTranslation(language);

        if (translation != null) {
            throw new TranslationException("Key " + keyString + " is a duplicate in language " + language);
        }

        return key;
    }

    private void translateIfRealTranslation(Key key, String translation, TranslationMode translationMode) throws TranslationException {
        String translationToUse = translation;
        String referenceTranslation = loadedLanguageIsReference() ? translation : key.getTranslation(referenceLanguage);

        if (translationMode == null) {
            boolean noNewTranslation = !currentLanguageIsReference() && Utils.equalsIgnoreCase(referenceTranslation, translationToUse);
            if (noNewTranslation || key.isDontTranslate()) {
                return;
            }
            key.translate(language, translationToUse);
        } else {
            switch (translationMode) {
                case AsReference:
                    key.translate(language, referenceTranslation);
                    break;
                case DontTranslate:
                    if (referenceTranslation != null) {
                        translationToUse = referenceTranslation;
                    }
                    if (currentLanguageIsReference()) {
                        key.translate(language, translationToUse);
                        key.setDontTranslate(true);
                    }
                    break;
            }
        }
    }

    private boolean loadedLanguageIsReference() {
        return referenceLanguage == null;
    }

    private boolean currentLanguageIsReference() {
        return Objects.equals(language, referenceLanguage) || referenceLanguage == null;
    }

    private Category ensureUniqueCategory(String categoryName) {
        if (!categoryMap.containsKey(categoryName)) {
            Category category = new Category(categoryName);
            categoryMap.put(categoryName, category);
        }

        return categoryMap.get(categoryName);
    }
}
